package com.luke.netty.sample;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @Descrtption NettyServer
 * @Author luke
 * @Date 2020/4/29
 **/
public class NettyServer {

    private static final int LISTEN_PORT = 8765;

    public static void main(String[] args) {

        //创建boss group 和 worker group
        //一个端口只需要一个bossGroup即可，因为对于连接来说，服务器端总是一个个
        //排队进来的，即Nio中的ServerSocketChannel.accept()
        //连接线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); //连接线程池，处理连接事件，默认大小是cpu核数*2，一般情况下只需要一个即可
        //I/O线程池
        EventLoopGroup workerGroup = new NioEventLoopGroup(1);//I/O线程池，处理读写事件的，默认大小是cpu核数*2

        //通常一个好的Netty服务端会有三个线程池：1、连接线程池（大小为：1）2、I/O线程池（大小为：cpu核数*2）3、业务线程池（大小根据客户端并发数）

        //创建启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //设置group
        serverBootstrap.group(bossGroup,workerGroup);

        //设置相关配置
        serverBootstrap.channel(NioServerSocketChannel.class)//表示Channel的实现类是NioServerSocketChannel，即服务端
                .option(ChannelOption.SO_BACKLOG,1024)//等待连接队列长度
                .option(ChannelOption.SO_KEEPALIVE,true)//保持活跃状态
                .childHandler(new ChannelInitializer<SocketChannel>() {//客户端连接上来的Channel是SocketChannel
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //客户端连接上来
                        //一个SocketChannel代表了一个客户端的连接
                        //一个SocketChannel（通道）的业务处理交给管道中的Hander来处理，每个SocketChannel对应着自己的一个管道
                        //管道Pipeline：通过SocketChannel可以获取到Pipeline，通过Pipeline也可以获取到SocketChannel

                        ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new NettyServerChannelHandler());//可以往管道中添加自己的通道处理器
                    }
                });

        try {
            //开始启动端口监听
            ChannelFuture channelFuture = serverBootstrap.bind(LISTEN_PORT).sync();
            System.out.println("服务端启动成功，监听端口："+LISTEN_PORT);

            //监听监听关闭事件
            Channel channel = channelFuture.channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}
